﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Diagnostics;

using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {
        const int SCALE = 2;

        SkinColorDatabase skinColorDatabase;            //skin color graph information
        SkinColorAreaDatabase skinColorAreaDatabase;    //skin color rules information

        Boolean addRule = false;    //true if the "add rule" button has been pressed
        Boolean firstRuleCoordinatePicked = false;

        public Form1()
        {
            InitializeComponent();

            skinColorAreaDatabase = new SkinColorAreaDatabase();
        }

        private void loadImageButton_Click(object sender, EventArgs e)
        {
            //from: http://www.devasp.net/net/articles/display/385.html
            try
            {
                OpenFileDialog openFileDialogImage = new OpenFileDialog();
                openFileDialogImage.Filter = "Image Files(*.jpg; *.jpeg; *.gif; *.bmp)|*.jpg; *.jpeg; *.gif; *.bmp";

                MessageBox.Show("Select image with skin pixels only");
                if (openFileDialogImage.ShowDialog() == DialogResult.OK)
                {
                    //load selected image             
                    imagePictureBox.Image = new Bitmap(openFileDialogImage.FileName);
                    imagePictureBox.Width = imagePictureBox.Image.Width;
                    imagePictureBox.Height = imagePictureBox.Image.Height;

                    //update the skin color database and redraw the graph
                    if(skinColorDatabase == null)skinColorDatabase = new SkinColorDatabase();
                    skinColorDatabase.AddColorsFromImage(imagePictureBox.Image);
                    drawGraph();
                }
            }
            catch
            {
                MessageBox.Show("Failed loading image");
            }
        }

        public void drawGraph()
        {
            int maxCb = (int)SkinColorAreaDatabase.MAX_CB;
            int maxCr = (int)SkinColorAreaDatabase.MAX_CR;

            int minCb = (int)SkinColorAreaDatabase.MIN_CB;
            int minCr = (int)SkinColorAreaDatabase.MIN_CR;

            Bitmap newGraphBitmap = new Bitmap(maxCb - minCb, maxCr - minCr);

            for (int i = 0; i < newGraphBitmap.Width; i++)
            {
                for (int j = 0; j < newGraphBitmap.Height; j++)
                {
                    newGraphBitmap.SetPixel(i, j, Color.Cyan);
                }
            }

            foreach (Color color in skinColorDatabase.SkinColors)
            {
                double[] tempYCbCrColor = (SkinColorAreaDatabase.ConvertColorToYCbCr(color));   //index 0 = Y, 1 = Cb, 2 = Cr
                newGraphBitmap.SetPixel((int)(tempYCbCrColor[1] - minCb), ((int)(tempYCbCrColor[2] - minCr)), color);
            }

            newGraphBitmap = new Bitmap(ResizeBMPImage((Image)newGraphBitmap, SCALE));
            graphPictureBox.Image = newGraphBitmap;
            graphPictureBox.Width = newGraphBitmap.Width;
            graphPictureBox.Height = newGraphBitmap.Height;
        }

        //from: http://www.dreamincode.net/code/snippet1986.htm
        public Image ResizeBMPImage(Image img, double scale)
        {
            //get the height and width of the image
            int originalW = img.Width;
            int originalH = img.Height;

            //get the new size based on the percentage change
            int resizedW = (int)(originalW * scale);
            int resizedH = (int)(originalH * scale);

            //create a new Bitmap the size of the new image
            Bitmap bmp = new Bitmap(resizedW, resizedH);
            //create a new graphic from the Bitmap
            Graphics graphic = Graphics.FromImage((Image)bmp);
            graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
            //draw the newly resized image
            graphic.DrawImage(img, 0, 0, resizedW, resizedH);
            //dispose and free up the resources
            graphic.Dispose();
            //return the image
            return (Image)bmp;
        }

        //load skin color graph data
        private void loadValuesButton_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog openFileDialogColors = new OpenFileDialog();
                MessageBox.Show("Select color database");
                if (openFileDialogColors.ShowDialog() == DialogResult.OK)
                {
                    Debug.Write("waha");
                    skinColorDatabase = new SkinColorDatabase();
                    skinColorDatabase.LoadFromFile(openFileDialogColors.FileName);
                    drawGraph();
                }
            }
            catch
            {
                MessageBox.Show("Failed loading image.");
            }
        }

        //save skin color graph data
        private void SaveValuesButton_Click(object sender, EventArgs e)
        {
            if (skinColorDatabase != null)
            {
                MessageBox.Show("Saving color database");
                SaveFileDialog saveFileDialogColors = new SaveFileDialog();
                if (saveFileDialogColors.ShowDialog() == DialogResult.OK)
                {
                    skinColorDatabase.SaveToFile(saveFileDialogColors.FileName);
                    Debug.Write("waha2");
                }
            }
            else
            {
                MessageBox.Show("Load or create a color database first!");
            }
        }

        //updates x and y labels and handles adding new rules
        private void graphPictureBox_Click(object sender, EventArgs e)
        {
            int x1 = (int)(Control.MousePosition.X - graphPictureBox.Location.X - this.Location.X - 4)/SCALE;
            int y1 = (int)(Control.MousePosition.Y - graphPictureBox.Location.Y - this.Location.Y - 30)/SCALE;

            if (addRule)
            {
                if (firstRuleCoordinatePicked)
                {
                    addRule = false;
                    firstRuleCoordinatePicked = false;

                    int x2 = Int32.Parse(xLabel.Text);
                    int y2 = Int32.Parse(yLabel.Text);

                    if (x1 > x2)
                    {
                        int tempX = x1;
                        x1 = x2;
                        x2 = tempX;
                    }

                    if (y1 > y2)
                    {
                        int tempY = y1;
                        y1 = y2;
                        y2 = tempY;
                    }

                    skinColorAreaDatabase.SkinColorAreas.Add(new Rectangle(x1,y1,x2-x1,y2-y1));
                    MessageBox.Show("Rule added!");
                }
                else
                {
                    firstRuleCoordinatePicked = true;
                    MessageBox.Show("Pick next coordinate.");
                }
            }
            xLabel.Text = "" + x1;
            yLabel.Text = "" + y1;
        }

        //load a test image to identify skin pixels from
        private void loadTestImageButton_Click(object sender, EventArgs e)
        {
            //from: http://www.devasp.net/net/articles/display/385.html
            if (skinColorAreaDatabase == null)
                MessageBox.Show("Load rule database first.");
            else
            {
                try
                {
                    OpenFileDialog openFileDialogImage = new OpenFileDialog();
                    openFileDialogImage.Filter = "Image Files(*.jpg; *.jpeg; *.gif; *.bmp)|*.jpg; *.jpeg; *.gif; *.bmp";

                    MessageBox.Show("Select image to identify skin pixels from.");
                    if (openFileDialogImage.ShowDialog() == DialogResult.OK)
                    {
                        //load selected image
                        Bitmap testImageBitmap = new Bitmap(openFileDialogImage.FileName);

                        testImagePictureBox.Image = testImageBitmap;
                        testImagePictureBox.Width = testImagePictureBox.Image.Width;
                        testImagePictureBox.Height = testImagePictureBox.Image.Height;

                        Bitmap skinPixelsBitmap = new Bitmap(testImagePictureBox.Image.Width, testImagePictureBox.Image.Height);
                        for (int i = 0; i < skinPixelsBitmap.Width; i++)
                        {
                            for (int j = 0; j < skinPixelsBitmap.Height; j++)
                            {
                                Color tempColor = testImageBitmap.GetPixel(i, j);
                                if (skinColorAreaDatabase.IsSkinPixel(tempColor))
                                    skinPixelsBitmap.SetPixel(i, j, tempColor);
                                else
                                    skinPixelsBitmap.SetPixel(i, j, Color.Cyan);
                            }
                        }

                        //draw the skin pixels
                        imagePictureBox.Image = skinPixelsBitmap;
                        imagePictureBox.Width = skinPixelsBitmap.Width;
                        imagePictureBox.Height = skinPixelsBitmap.Height;

                        Debug.Write("Skin pixels identified.");
                    }
                }
                catch
                {
                    MessageBox.Show("Failed loading image.");
                }
            }
        }

        //draw the rules in the cb-cr-graph
        private void drawRuleButton_Click(object sender, EventArgs e)
        {
            if (skinColorAreaDatabase == null)
                MessageBox.Show("Load rule database first.");
            else
            {
                Graphics picBoxGraphics = this.graphPictureBox.CreateGraphics();
                Pen p = new Pen(Color.Black, 1);

                foreach (Rectangle rect in skinColorAreaDatabase.SkinColorAreas)
                {
                    picBoxGraphics.DrawRectangle(p, new Rectangle(rect.X * SCALE, rect.Y * SCALE, rect.Width*SCALE, rect.Height*SCALE));
                }
                
                picBoxGraphics.Dispose();
                p.Dispose();
            }
        }

        private void addRuleButton_Click(object sender, EventArgs e)
        {
            addRule = true;
        }

        //delete last rule element (a rectangle containing skin pixels) in the skin color area list
        private void deleteRuleButton_Click(object sender, EventArgs e)
        {
            if(skinColorAreaDatabase.SkinColorAreas.Count > 0)
                skinColorAreaDatabase.SkinColorAreas.RemoveAt(skinColorAreaDatabase.SkinColorAreas.Count - 1);            
        }

        private void saveRuleButton_Click(object sender, EventArgs e)
        {
            if (skinColorAreaDatabase != null)
            {
                MessageBox.Show("Saving rule database");
                SaveFileDialog saveFileDialogColors = new SaveFileDialog();
                if (saveFileDialogColors.ShowDialog() == DialogResult.OK)
                {
                    skinColorAreaDatabase.SaveAreasToFile(saveFileDialogColors.FileName);
                }
            }
            else
            {
                MessageBox.Show("Load or create a rule database first!");
            }
        }

        private void loadRuleButton_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog openFileDialogColors = new OpenFileDialog();

                MessageBox.Show("Select rule database");
                if (openFileDialogColors.ShowDialog() == DialogResult.OK)
                {
                    skinColorAreaDatabase.LoadAreasFromFile(openFileDialogColors.FileName);
                }
            }
            catch
            {
                MessageBox.Show("Failed loading rules.");
            }
        }

        private void updateGraph_Click(object sender, EventArgs e)
        {
            if (skinColorDatabase != null) drawGraph();
        }
    }
}
